/*
 * ManejadorListaPalabras.cpp
 *
 *  Created on: 05/11/2010
 *      Author: julfc
 */

#include "ManejadorListaPalabras.h"

ManejadorListaPalabras::ManejadorListaPalabras(char* nombreArchivo, AbrirCrearArchivo abrirCrear):
											ManejadorAccesoDatos(nombreArchivo, abrirCrear) {
}


vector<IdMailPeso> ManejadorListaPalabras::leerLista(long nroBloque, long id) {

	char* bloque = leerBloque(nroBloque);
	char* auxBloque = bloque;
	long cantListas = 0;
	bool encontrado = false;
	vector<IdMailPeso> lista;

	auxBloque = bloque;
	memcpy(&cantListas, auxBloque, sizeof(long));
	//Me salteo el espacio libre en el bloque.
	auxBloque+=2*sizeof(long);

	long i = 1;

	while (i <= cantListas && !encontrado) {
		long uiAux = 0;
		long tamanioLista = 0;

		memcpy(&uiAux, auxBloque, sizeof(long));
		auxBloque+=sizeof(long);
		memcpy(&tamanioLista, auxBloque, sizeof(long));
		auxBloque+=sizeof(long);

		if (uiAux == id ) {
			encontrado = true;
			//long cantidadIds = tamanioLista/(sizeof(long) + MAX_BITS);
			long bytesLeidos = 0;
			for (int j = 0; bytesLeidos < tamanioLista; j++) {
				char* idMailChar = (char*) malloc(MAX_BITS + 1);
				idMailChar[MAX_BITS]='\0';
				long peso;
				memcpy(idMailChar, auxBloque, MAX_BITS);
				auxBloque+=strlen(idMailChar);
				bytesLeidos+= strlen(idMailChar);
				string distStr;
				distStr.assign(idMailChar);
				long idMail = decodearDistancia(distStr);
				memcpy(&peso,auxBloque,sizeof(long));
				auxBloque+=sizeof(long);
				bytesLeidos+=sizeof(long);
				if (j > 0) {
					idMail+= lista[j-1].idMail;
				}
				vector<long> pos;
				for (int k = 0; k < peso; k++) {
					long posAux = 0;
					memcpy(&posAux,auxBloque,sizeof(long));
					auxBloque+=sizeof(long);
					bytesLeidos+=sizeof(long);
					pos.push_back(posAux);
				}
				IdMailPeso mailPeso(idMail, peso, pos);
				lista.push_back(mailPeso);
				free(idMailChar);
			}
		} else {
			auxBloque+= tamanioLista;
			i++;
		}
	}
	free(bloque);
	return lista;
}

long ManejadorListaPalabras::escribirIdMailPeso(long id, IdMailPeso mailPeso) {
	vector<IdMailPeso> lista;
	lista.push_back(mailPeso);
	long nroBloque = escribirLista(id, lista);
	return nroBloque;
}

long ManejadorListaPalabras::escribirLista(long id, vector<IdMailPeso> lista) {
	long tamListasInternas = calcularTamListasInternas(lista, lista.size());
	long tamLista = sizeof(long) * lista.size() + lista.size() * MAX_BITS + tamListasInternas;
	long tamVector = lista.size();
	long tamBloque = getTamanioBloques();
	long tamMaximoListaEnBloque = tamBloque - 4*sizeof(long);
	if (tamLista > tamMaximoListaEnBloque) {
	  tamVector = calcularTamVector(lista, tamMaximoListaEnBloque - (sizeof(long) * lista.size() + lista.size() * MAX_BITS));
	  tamLista = tamVector * sizeof(long) + tamVector * MAX_BITS + calcularTamListasInternas(lista, tamVector);
	  cout << " \n\n Se trunco la lista porque no entra en el bloque, la lista es de " << tamVector << "\n\n";
	}

	long espLibreBloque = 0;
	long cantListas = 0;
	char* bloque;
	char* auxBloque;

	long nroBloque = buscarEspacioEnBloquesLista(tamLista);
	if (nroBloque < 0) {
		//Tengo que creabloquer un bloque nuevo
		nroBloque = getCantidadBloques();
		//cout << " \n\n No hay espacio libre en el bloque, se creo el bloque " << nroBloque << "\n\n";

		bloque = inicillizarBloque(tamBloque);
		auxBloque = bloque;
		//Guardo espacio libre (total - cant listas - esp_libre - id de la lista - cantidad de reg en lista - tam de la lista)).
		espLibreBloque = tamBloque - 4*sizeof(long) - tamLista;
		cantListas = 1;
		memcpy(auxBloque, &cantListas, sizeof(long));
		auxBloque+=sizeof(long);
		memcpy(auxBloque, &espLibreBloque, sizeof(long));
		auxBloque+=sizeof(long);
	} else {
		//cout << " \n\n Hay espacio libre en el bloque " << nroBloque << "\n\n";
		bloque = leerBloque(nroBloque);
		auxBloque = bloque;
		memcpy(&cantListas, auxBloque, sizeof(long));
		cantListas++;
		memcpy(auxBloque, &cantListas, sizeof(long));
		auxBloque+=sizeof(long);
		memcpy(&espLibreBloque, auxBloque, sizeof(long));
		long index = tamBloque - espLibreBloque;
		espLibreBloque = espLibreBloque - tamLista - (2*sizeof(long));
		memcpy(auxBloque, &espLibreBloque, sizeof(long));
		auxBloque = bloque;
		auxBloque+= index;
	}
	memcpy(auxBloque, &id, sizeof(long));
	auxBloque+=sizeof(long);
	memcpy(auxBloque, &tamLista, sizeof(long));
	auxBloque+=sizeof(long);
	for (int i = 0; i < tamVector; i++) {
		IdMailPeso mailPeso = lista[i];
		long auxDistId = mailPeso.idMail;
		if (i > 0) {
			 auxDistId-= lista[i-1].idMail;
		}
		string encodeDistId = encodearDistancia(auxDistId);
		const char* encodeDistIdStr = encodeDistId.c_str();
		memcpy(auxBloque, encodeDistIdStr, strlen(encodeDistIdStr));
		auxBloque+=strlen(encodeDistIdStr);
		memcpy(auxBloque, &(mailPeso.peso), sizeof(long));
		auxBloque+=sizeof(long);
		for (int j = 0; j < mailPeso.peso; j++) {
			long pos = mailPeso.pos[j];
			memcpy(auxBloque, &(pos), sizeof(long));
			auxBloque+=sizeof(long);
		}
	}
	escribir(bloque, nroBloque);
	free(bloque);
	return nroBloque;
}

long ManejadorListaPalabras::calcularTamVector(vector<IdMailPeso> lista, long tamMax) {
	long tamanio = 0;
	for (unsigned int i = 0; i < lista.size(); i++) {
		tamanio += (2 + lista[i].pos.size()) * sizeof(long);
		if (tamanio > tamMax) {
			return i-1;
		}
	}
	return lista.size();
}

bool ManejadorListaPalabras::eliminarIdDeLista(long id) {
	long cantBloques = getCantidadBloques();
	for (int i = 1; i < cantBloques; i++) {
		eliminarIdDeBloque(i, id);
	}
	return true;
}

bool ManejadorListaPalabras::eliminarIdDeBloque(long nroBloque, long id) {
	char* bloque = leerBloque(nroBloque);
	long tamBloque = getTamanioBloques();
	char* auxBloque = bloque;
	long cantListas = 0;
	long espLibreBloque = 0;
	long i = 0;
	long uiAux = 0;
	long tamanioLista = 0;
	long index = 0;
	bool estaElId = false;
	bool seModifico = false;

	memcpy(&cantListas, auxBloque, sizeof(long));
	auxBloque+= sizeof(long);
	memcpy(&espLibreBloque, auxBloque, sizeof(long));
	auxBloque+= sizeof(long);

	index+= 2*sizeof(long);
	char* aGuardar;

	while (i < cantListas) {
		vector<IdMailPeso> lista;
		estaElId = false;
		uiAux = 0;
		tamanioLista = 0;
		memcpy(&uiAux, auxBloque, sizeof(long));
		auxBloque+=sizeof(long);
		memcpy(&tamanioLista, auxBloque, sizeof(long));
		auxBloque+=sizeof(long);
		index+= 2*sizeof(long);
		//Obtengo la lista acomodo el bloque y la vuelvo a insertar.
		//long cantidadIds = tamanioLista/(sizeof(long) + MAX_BITS);
		long bytesLeidos = 0;
		long tamCantPos = 0;
		for (int j = 0; bytesLeidos < tamanioLista; j++) {
			long idMailDist;
			long peso;
			char* idMailChar = (char*) malloc(MAX_BITS + 1);
			idMailChar[MAX_BITS]='\0';
			memcpy(idMailChar, auxBloque, MAX_BITS);
			auxBloque+=strlen(idMailChar);
			bytesLeidos+=strlen(idMailChar);
			string distStr;
			distStr.assign(idMailChar);
			long idMail = decodearDistancia(distStr);
			memcpy(&peso,auxBloque,sizeof(long));
			auxBloque+=sizeof(long);
			bytesLeidos+=sizeof(long);
			if (j > 0) {
				idMail += idMailDist;
			}
			vector<long> pos;
			for (int k = 0; k < peso; k++) {
				long posAux = 0;
				memcpy(&posAux,auxBloque,sizeof(long));
				auxBloque+=sizeof(long);
				bytesLeidos+=sizeof(long);
				pos.push_back(posAux);
			}
			IdMailPeso idMailPeso(idMail, peso, pos);
			if (idMail == id) {
				estaElId = true;
				seModifico = true;
				idMailDist = idMail;
				tamCantPos = peso*sizeof(long);
			} else {
				lista.push_back(idMailPeso);
				idMailDist = idMail;
				tamCantPos = peso*sizeof(long);
			}
			index+=sizeof(long) + MAX_BITS + tamCantPos;
			free(idMailChar);
		}
		if (estaElId) {
			//actualizarListaDemails()
			char* nuevoBloque = inicillizarBloque(tamBloque);
			char* auxNuevoBloque = nuevoBloque;
			index-= 2*sizeof(long);
			index-=  tamanioLista;
			//Copio el cont. del nodo hasta antes de la lista a sacar para actualizar.
			auxBloque = bloque;
			//Salteo cantidad de listas en bloque.
			auxBloque+= sizeof(long);
			memcpy(&espLibreBloque, auxBloque, sizeof(long));
			//Solo sumo un sizeof(long) al espacio libre.
			espLibreBloque = espLibreBloque + sizeof(long) + MAX_BITS + tamCantPos;
			memcpy(auxBloque, &espLibreBloque, sizeof(long));
			//Vuelvo al principio del bloque.
			auxBloque-= sizeof(long);
			memcpy(auxNuevoBloque, auxBloque, index);
			auxNuevoBloque+= index;
			//Guardo de nuevo la lista sin el id que elimine.
			long nuevoTamLista = lista.size()*(sizeof(long) + MAX_BITS) + calcularTamListasInternas(lista, lista.size());
			memcpy(auxNuevoBloque, &uiAux, sizeof(long));
			auxNuevoBloque+=sizeof(long);
			memcpy(auxNuevoBloque, &nuevoTamLista, sizeof(long));
			auxNuevoBloque+=sizeof(long);

			for (unsigned int k = 0; k < lista.size(); k++) {
				IdMailPeso mailPesoAux = lista[k];
				long idMailAux = mailPesoAux.idMail;
				long pesoAux = mailPesoAux.peso;
				if (k > 0) {
					idMailAux-= lista[k-1].idMail;
				}
				string encodeDistId = encodearDistancia(idMailAux);
				const char* encodeDistIdStr = encodeDistId.c_str();
				memcpy(auxNuevoBloque, encodeDistIdStr, strlen(encodeDistIdStr));
				auxNuevoBloque+=strlen(encodeDistIdStr);
				memcpy(auxNuevoBloque, &(pesoAux), sizeof(long));
				auxNuevoBloque+=sizeof(long);
				for (int l = 0; l < mailPesoAux.peso; l++) {
					long pos = mailPesoAux.pos[l];
					memcpy(auxNuevoBloque, &(pos), sizeof(long));
					auxNuevoBloque+=sizeof(long);
				}
			}
			//Copio el resto de las cosas que habia en el bloque.
			auxBloque+= index + 2*sizeof(long) + tamanioLista;
			memcpy(auxNuevoBloque, auxBloque, tamBloque - index - 2*sizeof(long) - tamanioLista);
			//Escribo el nodo sin la lista.
			index+= (2*sizeof(long)) + nuevoTamLista;
			free(bloque);
			bloque = nuevoBloque;
			auxBloque = auxNuevoBloque;
			aGuardar = bloque;
			i++;
		} else {
			i++;
		}
	}
	if(seModifico) {
		escribir(aGuardar, nroBloque);
		free(aGuardar);
	}
	return seModifico;
}

long ManejadorListaPalabras::actualizarLista(long id, long nroBloque, IdMailPeso claveParaAgregar) {
	char* bloque = leerBloque(nroBloque);
	long tamBloque = getTamanioBloques();
	char* auxBloque = bloque;
	long nuevoNroBloque = -1;
	long cantListas = 0;
	long espLibreBloque = 0;
	bool encontrado = false;
	vector<IdMailPeso> lista;
	long i = 0;
	long uiAux = 0;
	long tamanioLista = 0;
	long index = 0;

	memcpy(&cantListas, auxBloque, sizeof(long));
	auxBloque+= sizeof(long);
	memcpy(&espLibreBloque, auxBloque, sizeof(long));
	auxBloque+= sizeof(long);

	index+= 2*sizeof(long);

	while (i <= cantListas && !encontrado) {
		uiAux = 0;
		tamanioLista = 0;
		memcpy(&uiAux, auxBloque, sizeof(long));
		auxBloque+=sizeof(long);
		memcpy(&tamanioLista, auxBloque, sizeof(long));
		auxBloque+=sizeof(long);
		index+= 2*sizeof(long);
		//Obtengo la lista acomodo el bloque y la vuelvo a insertar.
		if (uiAux == id ) {
			encontrado = true;
			//long cantidadIds = tamanioLista/(sizeof(long) + MAX_BITS);
			long bytesLeidos = 0;
			for (int j = 0; bytesLeidos < tamanioLista; j++) {
				long idMail;
				long peso;

				char* idMailChar = (char*) malloc(MAX_BITS + 1);
				idMailChar[MAX_BITS]='\0';
				memcpy(idMailChar, auxBloque, MAX_BITS);
				auxBloque+=strlen(idMailChar);
				bytesLeidos+=strlen(idMailChar);
				string distStr;
				distStr.assign(idMailChar);
				idMail = decodearDistancia(distStr);

				memcpy(&peso,auxBloque,sizeof(long));
				auxBloque+=sizeof(long);
				bytesLeidos+=sizeof(long);
				if (j > 0) {
					idMail+= lista[j-1].idMail;
				}
				vector<long> pos;
				for (int k = 0; k < peso; k++) {
					long posAux = 0;
					memcpy(&posAux,auxBloque,sizeof(long));
					auxBloque+=sizeof(long);
					bytesLeidos+=sizeof(long);
					pos.push_back(posAux);
				}
				IdMailPeso idMailPeso(idMail, peso, pos);
				lista.push_back(idMailPeso);
				index+=sizeof(long) + MAX_BITS + peso*sizeof(long);
				free(idMailChar);
			}
			lista.push_back(claveParaAgregar);

		} else {
			auxBloque+= tamanioLista;
			index+= tamanioLista;
			i++;
		}
	}

	//Si en encuentro el vector lo saco de la lista.
	if (encontrado) {
		char* nuevoBloque = inicillizarBloque(tamBloque);
		char* auxNuevoBloque = nuevoBloque;
		index-= 2*sizeof(long);
		index-=  tamanioLista;
		//Copio el cont. del nodo hasta antes de la lista a sacar para actualizar.
		auxBloque = bloque;

		memcpy(&cantListas, auxBloque, sizeof(long));
		cantListas--;
		memcpy(auxBloque, &cantListas, sizeof(long));
		auxBloque+= sizeof(long);
		memcpy(&espLibreBloque, auxBloque, sizeof(long));
		espLibreBloque = espLibreBloque + 2*sizeof(long) + tamanioLista;
		memcpy(auxBloque, &espLibreBloque, sizeof(long));

		//Vuelvo al principio del bloque.
		auxBloque-= sizeof(long);
		memcpy(auxNuevoBloque, auxBloque, index);
		auxBloque+= index + 2*sizeof(long) + tamanioLista;
		auxNuevoBloque+= index;
		memcpy(auxNuevoBloque, auxBloque, tamBloque - index - 2*sizeof(long) - tamanioLista);
		//Escribo el nodo sin la lista.
		escribir(nuevoBloque, nroBloque);
		free(nuevoBloque);
		nuevoNroBloque = escribirLista(id, lista);
	}

	free(bloque);
	return nuevoNroBloque;
}

//Defino este metodo de nuevo para que omita el primer bloque
//en el que voy a guardar la cantidad de listas.
long ManejadorListaPalabras::buscarEspacioEnBloquesLista(long tamanio) {
	long cantBloques = getCantidadBloques();
	long nro_bloque = -1;
	bool encontrado = false;
	long i = 1;
	char* bloque;
	while (i < cantBloques && !encontrado) {
		bloque = leerBloque(i);
		char* aux = bloque;
		long espacioLibre = 0;
		//Salteo cantidad de mails en bloque.
		aux += sizeof(long);
		memcpy(&espacioLibre, aux, sizeof(long));
		//uid + tamanio del mail + mail.
		if (espacioLibre >= (tamanio + 8)) {
			encontrado = true;
			nro_bloque = i;
		}
		i++;
		free(bloque);
	}
	return nro_bloque;
}

void ManejadorListaPalabras::inicCantidadListas() {
	long tamBloque = getTamanioBloques();
	char* bloque = inicillizarBloque(tamBloque);
	char* auxBloque = bloque;
	long cantListas = 0;
	memcpy(auxBloque, &cantListas, sizeof(long));
	escribir(bloque, 0);
	free(bloque);
}

void ManejadorListaPalabras::setCantidadListas(long cantListas) {
	char* bloque = leerBloque(0);
	char* auxBloque = bloque;
	memcpy(auxBloque, &cantListas, sizeof(long));
	escribir(bloque, 0);
	free(bloque);
}

long ManejadorListaPalabras::getCantidadListas() {
	char* bloque = leerBloque(0);
	long cantListas = 0;
	memcpy(&cantListas, bloque, sizeof(long));
	free(bloque);
	return cantListas;
}

string ManejadorListaPalabras::encodearDistancia(long dist) {
	DeltaCoder dc;
	string encodeada = dc.eliasDeltaEncode(dist);
	GeneradorBitset encoder;
	bitset<MAX_BITS> *comprimido = encoder.comprimir(encodeada);
	string resp = comprimido->to_string();
	delete(comprimido);
	return resp;
}

long ManejadorListaPalabras::decodearDistancia(string dist) {
	DeltaCoder dc;
	GeneradorBitset gb;
	bitset<MAX_BITS> comprimido(dist);
	string expandido = gb.expandir(&comprimido);
	long resp = dc.eliasDeltaDecode(expandido);
	return resp;
}

long ManejadorListaPalabras::calcularTamListasInternas(vector<IdMailPeso>lista, long poshasta) {
	long tam = 0;
	for (int i = 0; i < poshasta; i++) {
		tam+= lista[i].pos.size();
	}
	tam = tam * sizeof(long);
	return tam;
}

ManejadorListaPalabras::~ManejadorListaPalabras() {
	// TODO Auto-generated destructor stub
}
